This repository has no description
1import { error, redirect } from "@sveltejs/kit";
2import { createBobbinClient } from "$lib/api/client";
3import { resolveMiniDoc } from "$lib/api/identity";
4import { getProfile, type ProfileRecord } from "$lib/api/records";
5import { count } from "$lib/api/count";
6import { parallel, toHttpError, httpStatusFor } from "$lib/api/load";
7import { ClientResponseError } from "$lib/api/client";
8import { getFollowRkey } from "$lib/api/graph";
9import type { ProfileCounts } from "$lib/components/profile/types";
10import type { LayoutLoad } from "./$types";
11
12export const load: LayoutLoad = async (event) => {
13 const parent = await event.parent();
14 const identifier = decodeURIComponent(event.params.handle);
15
16 // rejects bare words so unrelated paths 404 instead of resolving as actors
17 if (!identifier.startsWith("did:") && !identifier.includes(".")) {
18 error(404, "Not found");
19 }
20
21 const ctx = createBobbinClient({ serviceUrl: parent.publicConfig.bobbinUrl, fetch: event.fetch });
22 const doc = await resolveMiniDoc(ctx, identifier).catch((cause) =>
23 toHttpError(cause, "Could not resolve user")
24 );
25
26 // redirects dids and stale handles to the canonical handle url
27 const canonical = doc.handle && !doc.handle.endsWith(".invalid") ? doc.handle : null;
28 if (canonical && identifier.toLowerCase() !== canonical.toLowerCase()) {
29 redirect(307, `/${canonical}${event.url.search}`);
30 }
31
32 const did = doc.did;
33 const viewerDid = parent.auth?.did;
34
35 let profile: ProfileRecord | null = null;
36 try {
37 profile = (await getProfile(ctx, did)).value;
38 } catch (cause) {
39 if (!(cause instanceof ClientResponseError && httpStatusFor(cause) === 404)) {
40 toHttpError(cause, "Could not load profile");
41 }
42 }
43
44 const raw = await parallel({
45 repos: count(ctx, "sh.tangled.repo.countRepos", did),
46 strings: count(ctx, "sh.tangled.string.countStrings", did),
47 stars: count(ctx, "sh.tangled.feed.countStarsBy", did),
48 followers: count(ctx, "sh.tangled.graph.countFollows", did),
49 following: count(ctx, "sh.tangled.graph.countFollowsBy", did),
50 vouches: count(ctx, "sh.tangled.graph.countVouches", did),
51 viewerFollowRkey:
52 viewerDid && viewerDid !== did
53 ? getFollowRkey(ctx, viewerDid, did).catch(() => null)
54 : Promise.resolve(null)
55 });
56
57 const counts: ProfileCounts = {
58 repos: raw.repos.count,
59 strings: raw.strings.count,
60 stars: raw.stars.count,
61 followers: raw.followers.count,
62 following: raw.following.count,
63 vouches: raw.vouches.count
64 };
65
66 const notJoined = !profile && Object.values(counts).every((n) => n === 0);
67
68 return {
69 identity: { did, handle: doc.handle, avatar: doc.avatar },
70 profile,
71 counts,
72 viewerFollowRkey: raw.viewerFollowRkey,
73 notJoined
74 };
75};